modernize nmea reader (#700)
authortsteven4 <13596209+tsteven4@users.noreply.github.com>
Thu, 4 Mar 2021 23:13:42 +0000 (16:13 -0700)
committerGitHub <noreply@github.com>
Thu, 4 Mar 2021 23:13:42 +0000 (16:13 -0700)
nmea.cc
nmea.h
reference/track/addmultipledays.csv [new file with mode: 0644]
reference/track/addmultipledaysbkw.nmea [new file with mode: 0644]
reference/track/addmultipledaysfwd.nmea [new file with mode: 0644]
reference/track/addmultipledaysmid.nmea [new file with mode: 0644]
testo.d/nmea.test
xmldoc/formats/options/nmea-pause.xml

diff --git a/nmea.cc b/nmea.cc
index 6be06f149087fde14fa183805e9f669bf6281d60..a5eee484e25f736eb31fadd282afd74e1cf82d66 100644 (file)
--- a/nmea.cc
+++ b/nmea.cc
 
  */
 
-#include <cctype>               // for isprint
-#include <cmath>                // for fabs, lround
-#include <cstdio>               // for snprintf, sscanf, NULL, fprintf, fputc, stderr
-#include <cstdlib>              // for atoi, atof, strtod
-#include <cstring>              // for strncmp, memset, strlen, strchr, strstr, strrchr
-#include <ctime>                // for time_t, gmtime
-#include <iterator>             // for operator!=, reverse_iterator
-
-#include <QtCore/QByteArray>    // for QByteArray
-#include <QtCore/QChar>         // for QChar, operator==, operator!=
-#include <QtCore/QCharRef>      // for QCharRef
-#include <QtCore/QDateTime>     // for QDateTime
-#include <QtCore/QDebug>        // for QDebug
-#include <QtCore/QList>         // for QList
-#include <QtCore/QString>       // for QString, QString::KeepEmptyParts
-#include <QtCore/QStringList>   // for QStringList
-#include <QtCore/QTextStream>   // for hex
-#include <QtCore/QThread>       // for QThread
-#include <QtCore/QTime>         // for QTime
-#include <QtCore/QtGlobal>      // for qPrintable, foreach
+#include <cctype>                  // for isprint
+#include <cmath>                   // for fabs
+#include <cstdio>                  // for snprintf, sscanf, fprintf, fputc, stderr
+#include <cstdlib>                 // for atoi, atof, strtod
+#include <cstring>                 // for strncmp, strchr, strlen, strstr, memset, strrchr
+#include <iterator>                // for operator!=, reverse_iterator
+
+#include <QtCore/QByteArray>       // for QByteArray
+#include <QtCore/QChar>            // for QChar, operator==, operator!=
+#include <QtCore/QDateTime>        // for QDateTime
+#include <QtCore/QDebug>           // for QDebug
+#include <QtCore/QList>            // for QList
+#include <QtCore/QString>          // for QString, QString::KeepEmptyParts
+#include <QtCore/QStringList>      // for QStringList
+#include <QtCore/QTextStream>      // for hex
+#include <QtCore/QThread>          // for QThread
+#include <QtCore/QTime>            // for QTime
+#include <QtCore/Qt>               // for UTC
+#include <QtCore/QtGlobal>         // for qPrintable, foreach
 
 #include "defs.h"
 #include "nmea.h"
@@ -49,7 +48,6 @@
 #include "jeeps/gpsmath.h"         // for GPS_Lookup_Datum_Index, GPS_Math_Known_Datum_To_WGS84_M
 #include "src/core/datetime.h"     // for DateTime
 #include "src/core/logging.h"      // for Warning
-#include "strptime.h"              // for strptime
 
 
 /**********************************************************
@@ -223,9 +221,8 @@ NmeaFormat::rd_init(const QString& fname)
 {
   curr_waypt = nullptr;
   last_waypt = nullptr;
-  last_time = -1;
   datum = DATUM_WGS84;
-  had_checksum = 0;
+  had_checksum = false;
 
   CHECK_BOOL(opt_gprmc);
   CHECK_BOOL(opt_gpgga);
@@ -291,12 +288,12 @@ NmeaFormat::wr_init(const QString& fname)
 
   file_out = gbfopen(fname, append_output ? "a+" : "w+", MYNAME);
 
-  sleepus = -1;
+  sleepms = -1;
   if (opt_sleep) {
     if (*opt_sleep) {
-      sleepus = 1e6 * atof(opt_sleep);
+      sleepms = 1e3 * atof(opt_sleep);
     } else {
-      sleepus = -1;
+      sleepms = -1;
     }
   }
 
@@ -318,32 +315,50 @@ NmeaFormat::wr_deinit()
 }
 
 void
-NmeaFormat::nmea_set_waypoint_time(Waypoint* wpt, struct tm* time, double fsec)
+NmeaFormat::nmea_set_waypoint_time(Waypoint* wpt, QDateTime* prev, const QDate& date, const QTime& time)
 {
-  if (time->tm_year == 0) {
-    wpt->SetCreationTime(((((time_t)time->tm_hour * 60) + time->tm_min) * 60) + time->tm_sec, lround(1000.0 * fsec));
-    if (wpt->wpt_flags.fmt_use == 0) {
-      wpt->wpt_flags.fmt_use = 1;
-      without_date++;
+  if (date.isValid()) {
+    wpt->SetCreationTime(QDateTime(date, time, Qt::UTC));
+    if (wpt->wpt_flags.fmt_use != 0) {
+      wpt->wpt_flags.fmt_use = 0;
+      without_date--;
+    }
+    *prev = wpt->GetCreationTime();
+  } else if (prev->date().isValid()) {
+    wpt->SetCreationTime(QDateTime(prev->date(), time, Qt::UTC));
+    if (*prev > wpt->creation_time) {
+      /* go over midnight ? */
+      wpt->creation_time = wpt->creation_time.addDays(1);
     }
-  } else {
-    wpt->SetCreationTime(mkgmtime(time), lround(1000.0 * fsec));
     if (wpt->wpt_flags.fmt_use != 0) {
       wpt->wpt_flags.fmt_use = 0;
       without_date--;
     }
+    *prev = wpt->GetCreationTime();
+  } else {
+    wpt->SetCreationTime(QDateTime(QDate(), time, Qt::UTC));
+    if (wpt->wpt_flags.fmt_use == 0) {
+      wpt->wpt_flags.fmt_use = 1;
+      without_date++;
+    }
   }
 }
 
+QTime NmeaFormat::nmea_parse_hms(const QString& str)
+{
+  QString format = str.contains('.')? "hhmmss.zzz" : "hhmmss";
+  return QTime::fromString(str, format);
+}
+
 void
-NmeaFormat::gpgll_parse(char* ibuf)
+NmeaFormat::gpgll_parse(const QString& ibuf)
 {
   if (trk_head == nullptr) {
     trk_head = new route_head;
     track_add_head(trk_head);
   }
 
-  QStringList fields = QString(ibuf).split(",", QString::KeepEmptyParts);
+  const QStringList fields = ibuf.split(',', QString::KeepEmptyParts);
 
   double latdeg = 0;
   if (fields.size() > 1) latdeg = fields[1].toDouble();
@@ -353,8 +368,8 @@ NmeaFormat::gpgll_parse(char* ibuf)
   if (fields.size() > 3) lngdeg = fields[3].toDouble();
   QChar lngdir = 'E';
   if (fields.size() > 4) lngdir = fields[4][0];
-  double hmsd = 0;
-  if (fields.size() > 5) hmsd = fields[5].toDouble();
+  QTime hms;
+  if (fields.size() > 5) hms = nmea_parse_hms(fields[5]);
   bool valid = false;
   if (fields.size() > 6) valid = fields[6].startsWith('A');
 
@@ -362,19 +377,11 @@ NmeaFormat::gpgll_parse(char* ibuf)
     return;
   }
 
-  int hms = (int) hmsd;
   last_read_time = hms;
-  double fsec = hmsd - hms;
-
-  tm.tm_sec = hms % 100;
-  hms = hms / 100;
-  tm.tm_min = hms % 100;
-  hms = hms / 100;
-  tm.tm_hour = hms % 100;
 
   Waypoint* waypt = nmea_new_wpt();
 
-  nmea_set_waypoint_time(waypt, &tm, fsec);
+  nmea_set_waypoint_time(waypt, &prev_datetime, QDate(), hms);
 
   if (latdir == 'S') {
     latdeg = -latdeg;
@@ -391,16 +398,16 @@ NmeaFormat::gpgll_parse(char* ibuf)
 }
 
 void
-NmeaFormat::gpgga_parse(char* ibuf)
+NmeaFormat::gpgga_parse(const QString& ibuf)
 {
   if (trk_head == nullptr) {
     trk_head = new route_head;
     track_add_head(trk_head);
   }
 
-  QStringList fields = QString(ibuf).split(",", QString::KeepEmptyParts);
-  double hms = 0;
-  if (fields.size() > 1) hms = fields[1].toDouble();
+  const QStringList fields = ibuf.split(',', QString::KeepEmptyParts);
+  QTime hms;
+  if (fields.size() > 1) hms = nmea_parse_hms(fields[1]);
   double latdeg = 0;
   if (fields.size() > 2) latdeg = fields[2].toDouble();
   QChar latdir = 'N';
@@ -413,8 +420,8 @@ NmeaFormat::gpgga_parse(char* ibuf)
   if (fields.size() > 6) fix = fields[6].toInt();
   int nsats = 0;
   if (fields.size() > 7) nsats = fields[7].toInt();
-  double hdop = 0;
-  if (fields.size() > 8) hdop = fields[8].toDouble();
+  float hdop = 0;
+  if (fields.size() > 8) hdop = fields[8].toFloat();
   double alt = unknown_alt;
   if (fields.size() > 9) alt = fields[9].toDouble();
   QChar altunits ='M';
@@ -436,17 +443,10 @@ NmeaFormat::gpgga_parse(char* ibuf)
   }
 
   last_read_time = hms;
-  double fsec = hms - (int)hms;
-
-  tm.tm_sec = (long) hms % 100;
-  hms = hms / 100;
-  tm.tm_min = (long) hms % 100;
-  hms = hms / 100;
-  tm.tm_hour = (long) hms % 100;
 
   Waypoint* waypt = nmea_new_wpt();
 
-  nmea_set_waypoint_time(waypt, &tm, fsec);
+  nmea_set_waypoint_time(waypt, &prev_datetime, QDate(), hms);
 
   if (latdir == 'S') {
     latdeg = -latdeg;
@@ -488,16 +488,16 @@ NmeaFormat::gpgga_parse(char* ibuf)
 }
 
 void
-NmeaFormat::gprmc_parse(char* ibuf)
+NmeaFormat::gprmc_parse(const QString& ibuf)
 {
   if (trk_head == nullptr) {
     trk_head = new route_head;
     track_add_head(trk_head);
   }
 
-  QStringList fields = QString(ibuf).split(",", QString::KeepEmptyParts);
-  double hms = 0;
-  if (fields.size() > 1) hms = fields[1].toDouble();
+  const QStringList fields = ibuf.split(',', QString::KeepEmptyParts);
+  QTime hms;
+  if (fields.size() > 1) hms = nmea_parse_hms(fields[1]);
   QChar fix = 'V'; // V == "Invalid"
   if (fields.size() > 2) fix = fields[2][0];
   double latdeg = 0;
@@ -512,28 +512,18 @@ NmeaFormat::gprmc_parse(char* ibuf)
   if (fields.size() > 7) speed = fields[7].toDouble();
   double course = 0;
   if (fields.size() > 8) course = fields[8].toDouble();
-  int dmy = 0;
-  if (fields.size() > 9) dmy = fields[9].toDouble();
-
+  QDate dmy;
+  if (fields.size() > 9) {
+    QString datestr(fields[9]);
+    datestr.insert(4, "20");
+    dmy = QDate::fromString(datestr, "ddMMyyyy");
+  }
   if (fix != 'A') {
     /* ignore this fix - it is invalid */
     return;
   }
 
   last_read_time = hms;
-  double fsec = hms - (int)hms;
-
-  tm.tm_sec = (long) hms % 100;
-  hms = hms / 100;
-  tm.tm_min = (long) hms % 100;
-  hms = hms / 100;
-  tm.tm_hour = (long) hms % 100;
-
-  tm.tm_year = dmy % 100 + 100;
-  dmy = dmy / 100;
-  tm.tm_mon  = dmy % 100 - 1;
-  dmy = dmy / 100;
-  tm.tm_mday = dmy;
 
   if (posn_type == gpgga) {
     /* capture useful data update and exit */
@@ -546,7 +536,7 @@ NmeaFormat::gprmc_parse(char* ibuf)
       }
       /* The change of date wasn't recorded when
        * going from 235959 to 000000. */
-      nmea_set_waypoint_time(curr_waypt, &tm, fsec);
+      nmea_set_waypoint_time(curr_waypt, &prev_datetime, dmy, hms);
     }
     /* This point is both a waypoint and a trackpoint. */
     if (amod_waypoint) {
@@ -561,7 +551,7 @@ NmeaFormat::gprmc_parse(char* ibuf)
   WAYPT_SET(waypt, speed, KNOTS_TO_MPS(speed));
   WAYPT_SET(waypt, course, course);
 
-  nmea_set_waypoint_time(waypt, &tm, fsec);
+  nmea_set_waypoint_time(waypt, &prev_datetime, dmy, hms);
 
   if (latdir == 'S') {
     latdeg = -latdeg;
@@ -584,13 +574,9 @@ NmeaFormat::gprmc_parse(char* ibuf)
 }
 
 void
-NmeaFormat::gpwpl_parse(char* ibuf)
+NmeaFormat::gpwpl_parse(const QString& ibuf)
 {
-  // The last field isn't actually separated by a field separator and
-  // is a string, so we brutally whack the checksum (trailing *NN).
-  QString qibuf = QString(ibuf);
-  qibuf.truncate(qibuf.lastIndexOf('*'));
-  QStringList fields = qibuf.split(",", QString::KeepEmptyParts);
+  const QStringList fields = ibuf.split(',', QString::KeepEmptyParts);
 
   double latdeg = 0;
   if (fields.size() > 1) latdeg = fields[1].toDouble();
@@ -620,21 +606,18 @@ NmeaFormat::gpwpl_parse(char* ibuf)
 }
 
 void
-NmeaFormat::gpzda_parse(char* ibuf)
+NmeaFormat::gpzda_parse(const QString& ibuf)
 {
-  double hms;
-  int dd, mm, yy, lclhrs, lclmins;
-
-  sscanf(ibuf,"$%*2cZDA,%lf,%d,%d,%d,%d,%d",
-         &hms, &dd, &mm, &yy, &lclhrs, &lclmins);
-  tm.tm_sec  = (int) hms % 100;
-  tm.tm_min  = (((int) hms - tm.tm_sec) / 100) % 100;
-  tm.tm_hour = (int) hms / 10000;
-  tm.tm_mday = dd;
-  tm.tm_mon  = mm - 1;
-  tm.tm_year = yy - 1900;
-  // FIXME: why do we do all this and then do nothing with the result?
-  // This can't have worked.
+  const QStringList fields = ibuf.split(',', QString::KeepEmptyParts);
+  if (fields.size() > 4) {
+    QTime time = nmea_parse_hms(fields[1]);
+    QString datestr = QString("%1%2%3").arg(fields[2], fields[3], fields[4]);
+    QDate date = QDate::fromString(datestr, "ddMMyyyy");
+
+    // The prev_datetime data member might be used by
+    // nmea_fix_timestamps and nmea_set_waypoint_time.
+    prev_datetime = QDateTime(date, time, Qt::UTC);
+  }
 }
 
 // This function has had a hard life. It began as a moderately terrifying
@@ -646,12 +629,12 @@ NmeaFormat::gpzda_parse(char* ibuf)
 // The numbering as per http://aprs.gids.nl/nmea/#gsa was the reference as
 // the field numbers conveniently match our index.
 void
-NmeaFormat::gpgsa_parse(char* ibuf) const
+NmeaFormat::gpgsa_parse(const QString& ibuf) const
 {
   int  prn[12] = {0};
   memset(prn,0xff,sizeof(prn));
 
-  QStringList fields = QString(ibuf).split(",", QString::KeepEmptyParts);
+  const QStringList fields = ibuf.split(',', QString::KeepEmptyParts);
   int nfields = fields.size();
   // 0 = "GPGSA"
   // 1 = Mode. Ignored
@@ -668,11 +651,7 @@ NmeaFormat::gpgsa_parse(char* ibuf) const
   float pdop = 0, hdop = 0, vdop = 0;
   if (nfields > 15) pdop = fields[15].toFloat();
   if (nfields > 16) hdop = fields[16].toFloat();
-  if (nfields > 17) {
-     // Last one is special. The checksum isn't split out above.
-    fields[17].chop(3);
-    vdop = fields[17].toFloat();
-  }
+  if (nfields > 17) vdop = fields[17].toFloat();
 
   if (curr_waypt) {
     if (curr_waypt->fix!=fix_dgps) {
@@ -697,9 +676,9 @@ NmeaFormat::gpgsa_parse(char* ibuf) const
 }
 
 void
-NmeaFormat::gpvtg_parse(char* ibuf) const
+NmeaFormat::gpvtg_parse(const QString& ibuf) const
 {
-  QStringList fields = QString(ibuf).split(",", QString::KeepEmptyParts);
+  const QStringList fields = ibuf.split(',', QString::KeepEmptyParts);
   double course = 0;
   if (fields.size() > 1) course = fields[1].toDouble();
   double speed_n = 0;
@@ -730,7 +709,7 @@ double NmeaFormat::pcmpt_deg(int d)
 }
 
 void
-NmeaFormat::pcmpt_parse(char* ibuf)
+NmeaFormat::pcmpt_parse(const char* ibuf)
 {
   int i, j1, j2, j3, j4, j5, j6;
   int lat, lon;
@@ -772,18 +751,21 @@ NmeaFormat::pcmpt_parse(char* ibuf)
     curr_waypt->longitude = pcmpt_deg(lon);
     curr_waypt->latitude = pcmpt_deg(lat);
 
-    tm.tm_sec = (long) hms % 100;
+    int sec = hms % 100;
     hms = hms / 100;
-    tm.tm_min = (long) hms % 100;
+    int min = hms % 100;
     hms = hms / 100;
-    tm.tm_hour = (long) hms % 100;
+    int hour =  hms % 100;
+    QTime time = QTime(hour, min, sec);
 
-    tm.tm_year = dmy % 10000 - 1900;
+    int year = dmy % 10000;
     dmy = dmy / 10000;
-    tm.tm_mon  = dmy % 100 - 1;
+    int mon  = dmy % 100;
     dmy = dmy / 100;
-    tm.tm_mday = dmy;
-    nmea_set_waypoint_time(curr_waypt, &tm, 0);
+    int mday = dmy;
+    QDate date = QDate(year, mon, mday);
+
+    nmea_set_waypoint_time(curr_waypt, &prev_datetime, date, time);
     pcmpt_head.prepend(curr_waypt);
   } else {
 
@@ -812,29 +794,27 @@ NmeaFormat::nmea_fix_timestamps(route_head* track)
     return;
   }
 
-  if (tm.tm_year == 0) {
-    Waypoint* prev = nullptr;
-
+  if (!prev_datetime.date().isValid()) {
     if (optdate == nullptr) {
       warning(MYNAME ": No date found within track (all points dropped)!\n");
       warning(MYNAME ": Please use option \"date\" to preset a valid date for those tracks.\n");
       track_del_head(track);
       return;
     }
-    time_t delta_tm = mkgmtime(&opt_tm);
+
+    QDateTime prev = QDateTime(opt_tm, QTime(0, 0), Qt::UTC);
 
     foreach (Waypoint* wpt, track->waypoint_list) {
 
-      wpt->creation_time = wpt->creation_time.addSecs(delta_tm);
-      if ((prev != nullptr) && (prev->creation_time > wpt->creation_time)) {
+      wpt->creation_time.setDate(prev.date());
+      if (prev > wpt->creation_time) {
         /* go over midnight ? */
-        delta_tm += SECONDS_PER_DAY;
-        wpt->creation_time = wpt->creation_time.addSecs(SECONDS_PER_DAY);
+        wpt->creation_time = wpt->creation_time.addDays(1);
       }
-      prev = wpt;
+      prev = wpt->GetCreationTime();
     }
   } else {
-    time_t prev = mkgmtime(&tm);
+    QDateTime prev = prev_datetime;
 
     /* go backward through the track and complete timestamps */
 
@@ -844,70 +824,79 @@ NmeaFormat::nmea_fix_timestamps(route_head* track)
       if (wpt->wpt_flags.fmt_use != 0) {
         wpt->wpt_flags.fmt_use = 0; /* reset flag */
 
-        time_t dt = (prev / SECONDS_PER_DAY) * SECONDS_PER_DAY;
-        wpt->creation_time = wpt->creation_time.addSecs(dt);
-        if (wpt->creation_time.toTime_t() > prev) {
-          wpt->creation_time = wpt->creation_time.addSecs(-SECONDS_PER_DAY);
+        wpt->creation_time.setDate(prev.date());
+        if (wpt->creation_time > prev) {
+          wpt->creation_time = wpt->creation_time.addDays(-1);
         }
       }
-      prev = wpt->GetCreationTime().toTime_t();
+      prev = wpt->GetCreationTime();
     }
   }
 }
 
-int
-NmeaFormat::notalkerid_strmatch(const char * s1, const char *sentenceFormatterMnemonicCode)
+bool
+NmeaFormat::notalkerid_strmatch(const QByteArray& s1, const char *sentenceFormatterMnemonicCode)
 {
 /*
- * compare leading start of parametric sentence character ('$'), sentence address field, and trailing comma
- * to the desired sentence formatter mnemonic code (the 3rd-5th characters of the sentence address field).
- * The talker identifier mnemonic(the 1st-2nd characters of the sentence address field)
- * is likely "GP" for Global Positioning System (GPS)
- * but other talkers like "IN" for Integrated Navigation can emit relevant sentences,
- * so we ignore the talker identifier mnemonic.
+ * compare leading start of parametric sentence character ('$'), sentence
+ * address field, and trailing comma to the desired sentence formatter mnemonic
+ * code (the 3rd-5th characters of the sentence address field).  The talker
+ * identifier mnemonic (the 1st-2nd characters of the sentence address field)
+ * is likely "GP" for Global Positioning System (GPS) but other talkers like
+ * "IN" for Integrated Navigation can emit relevant sentences, so we ignore the
+ * talker identifier mnemonic.
  */
-return strncmp(s1,"$",1) || strncmp(s1+3,sentenceFormatterMnemonicCode,3) || strncmp(s1+6,",",1);
+  return (s1.size() > 6) && (s1.at(0) == '$') && (s1.at(6) == ',') &&
+         (s1.mid(3,3) == sentenceFormatterMnemonicCode);
 }
 
 void
-NmeaFormat::nmea_parse_one_line(char* ibuf)
+NmeaFormat::nmea_parse_one_line(const QByteArray& ibuf)
 {
-  char* tbuf = lrtrim(ibuf);
+  QByteArray tbuf = ibuf.trimmed();
 
   /*
    * GISTEQ PhotoTracker (stupidly) puts a bogus field in front
    * of the line.  Look for it and toss it.
    */
-  if (0 == strncmp(tbuf, "---,", 4)) {
-    tbuf += 4;
+  if (tbuf.startsWith("---,")) {
+    tbuf.remove(0, 4);
   }
 
-  if (*tbuf != '$') {
+  if (tbuf.at(0) != '$') {
     return;
   }
 
-  char* ck = strrchr(tbuf, '*');
-  if (ck != nullptr) {
-    *ck = '\0';
-    int ckval = nmea_cksum(&tbuf[1]);
-    *ck = '*';
-    ck++;
-    int ckcmp;
-    sscanf(ck, "%2X", &ckcmp);
-    if (ckval != ckcmp) {
-      Warning().nospace() <<  hex << "Invalid NMEA checksum.  Computed 0x" << ckval << " but found 0x" << ckcmp << ".  Ignoring sentence.";
+  if (int ckidx = tbuf.lastIndexOf('*'); ckidx >= 0) {
+    bool checked = false;
+    if ((ckidx + 2) < tbuf.size()) {
+      QByteArray ckstring = tbuf.mid(ckidx + 1, 2);
+      bool ok;
+      int ckcmp = ckstring.toInt(&ok, 16);
+      if (ok) {
+        int ckval = nmea_cksum(tbuf.mid(1, ckidx - 1));
+        if (ckval != ckcmp) {
+          Warning().nospace() <<  hex << "Invalid NMEA checksum.  Computed 0x" << ckval << " but found 0x" << ckcmp << ".  Ignoring sentence.";
+          return;
+        }
+        checked = true;
+      }
+    }
+    if (!checked) {
+      Warning().nospace()  << "Unrecoverable NMEA checksum in line " << tbuf << ". Ignoring sentence.";
       return;
     }
-
-    had_checksum = 1;
+    // hide checksum from sentence parsers.
+    tbuf.truncate(ckidx);
+    had_checksum = true;
   } else if (had_checksum) {
     /* we have had a checksum on all previous sentences, but not on this
     one, which probably indicates this line is truncated */
-    had_checksum = 0;
+    had_checksum = false;
     return;
   }
 
-  if (strstr(tbuf+1,"$")!=nullptr) {
+  if (tbuf.count("$") > 1) {
     /* If line has more than one $, there is probably an error in it. */
     return;
   }
@@ -917,16 +906,14 @@ NmeaFormat::nmea_parse_one_line(char* ibuf)
      for that field.  Rather than change all the parse routines, we first
      substitute a default value of zero for any missing field.
   */
-  if (strstr(tbuf, ",,")) {
-    tbuf = gstrsub(tbuf, ",,", ",0,");
-  }
+  tbuf.replace(",,", ",0,");
 
-  if (0 == notalkerid_strmatch(tbuf, "WPL")) {
+  if (notalkerid_strmatch(tbuf, "WPL")) {
     gpwpl_parse(tbuf);
-  } else if (opt_gpgga && (0 == notalkerid_strmatch(tbuf, "GGA"))) {
+  } else if (opt_gpgga && notalkerid_strmatch(tbuf, "GGA")) {
     posn_type = gpgga;
     gpgga_parse(tbuf);
-  } else if (opt_gprmc && (0 == notalkerid_strmatch(tbuf, "RMC"))) {
+  } else if (opt_gprmc && notalkerid_strmatch(tbuf, "RMC")) {
     if (posn_type != gpgga) {
       posn_type = gprmc;
     }
@@ -935,40 +922,35 @@ NmeaFormat::nmea_parse_one_line(char* ibuf)
      * it contains the full date.
      */
     gprmc_parse(tbuf);
-  } else if (0 == notalkerid_strmatch(tbuf, "GLL")) {
+  } else if (notalkerid_strmatch(tbuf, "GLL")) {
     if ((posn_type != gpgga) && (posn_type != gprmc)) {
       gpgll_parse(tbuf);
     }
-  } else if (0 == notalkerid_strmatch(tbuf, "ZDA")) {
+  } else if (notalkerid_strmatch(tbuf, "ZDA")) {
     gpzda_parse(tbuf);
-  } else if (0 == strncmp(tbuf, "$PCMPT,", 7)) {
-    pcmpt_parse(tbuf);
-  } else if (opt_gpvtg && (0 == notalkerid_strmatch(tbuf, "VTG"))) {
+  } else if (tbuf.startsWith("$PCMPT,")) {
+    pcmpt_parse(tbuf.constData());
+  } else if (opt_gpvtg && notalkerid_strmatch(tbuf, "VTG")) {
     gpvtg_parse(tbuf); /* speed and course */
-  } else if (opt_gpgsa && (0 == notalkerid_strmatch(tbuf, "GSA"))) {
+  } else if (opt_gpgsa && notalkerid_strmatch(tbuf, "GSA")) {
     gpgsa_parse(tbuf); /* GPS fix */
-  } else if (0 == strncmp(tbuf, "$ADPMB,5,0", 10)) {
+  } else if (tbuf.startsWith("$ADPMB,5,0")) {
     amod_waypoint = true;
   }
-
-  if (tbuf != ibuf) {
-    /* clear up the dynamic buffer we used because substitution was required */
-    xfree(tbuf);
-  }
 }
 
 void
 NmeaFormat::read()
 {
   char* ibuf;
-  double lt = -1;
+  QTime lt;
   int line = -1;
 
   posn_type = gp_unknown;
   trk_head = nullptr;
   without_date = 0;
-  memset(&tm, 0, sizeof(tm));
-  opt_tm = tm;
+  prev_datetime = QDateTime();
+  opt_tm = QDate();
 
   /* This was done in rd_init() */
   if (getposn) {
@@ -976,13 +958,9 @@ NmeaFormat::read()
   }
 
   if (optdate) {
-    memset(&opt_tm, 0, sizeof(opt_tm));
-
-    char* ck = strptime(optdate, "%Y%m%d", &opt_tm);
-    if ((ck == nullptr) || (*ck != '\0') || (strlen(optdate) != 8)) {
+    opt_tm = QDate::fromString(optdate, "yyyyMMdd");
+    if (!opt_tm.isValid()) {
       fatal(MYNAME ": Invalid date \"%s\"!\n", optdate);
-    } else if (opt_tm.tm_year < 70) {
-      fatal(MYNAME ": Date \"%s\" is out of range (have to be 19700101 or later)!\n", optdate);
     }
   }
 
@@ -1108,7 +1086,7 @@ Waypoint*
 NmeaFormat::rd_position(posn_status* /*unused*/)
 {
   char ibuf[1024];
-  static double lt = -1;
+  static QTime lt;
   int am_sirf = 0;
 
   /*
@@ -1143,7 +1121,7 @@ NmeaFormat::rd_position(posn_status* /*unused*/)
     }
     nmea_parse_one_line(ibuf);
     if (lt != last_read_time) {
-      if (last_read_time) {
+      if (last_read_time.isValid()) {
         Waypoint* w = curr_waypt;
 
         lt = last_read_time;
@@ -1182,16 +1160,16 @@ NmeaFormat::nmea_wayptpr(const Waypoint* wpt) const
           );
   int cksum = nmea_cksum(obuf);
   gbfprintf(file_out, "$%s*%02X\n", obuf, cksum);
-  if (sleepus >= 0) {
+  if (sleepms >= 0) {
     gbfflush(file_out);
-    QThread::usleep(sleepus);
+    QThread::msleep(sleepms);
   }
 }
 
 void
 NmeaFormat::nmea_track_init(const route_head* /*unused*/)
 {
-  last_time = -1;
+  first_trkpt = true;
 }
 
 void
@@ -1203,17 +1181,19 @@ NmeaFormat::nmea_trackpt_pr(const Waypoint* wpt)
 
   if (opt_sleep) {
     gbfflush(file_out);
-    if (last_time > 0) {
-      if (sleepus >= 0) {
-        QThread::usleep(sleepus);
+    if (!first_trkpt) {
+      if (sleepms >= 0) {
+        QThread::msleep(sleepms);
       } else {
-        long wait_time = wpt->GetCreationTime().toTime_t() - last_time;
+        // wait_time will be 0 if either creation time is invalid.
+        long wait_time = last_write_time.msecsTo(wpt->GetCreationTime());
         if (wait_time > 0) {
-          QThread::usleep(wait_time * 1000000);
+          QThread::msleep(wait_time);
         }
       }
     }
-    last_time = wpt->GetCreationTime().toTime_t();
+    last_write_time = wpt->GetCreationTime();
+    first_trkpt = false;
   }
 
   double lat = degrees2ddmm(wpt->latitude);
@@ -1389,6 +1369,6 @@ void NmeaFormat::reset_sirf_to_nmea(int br)
   pkt[27] = br & 0xff;
 
   sirf_write(pkt);
-  QThread::usleep(250 * 1000);
+  QThread::msleep(250);
   gbser_flush(gbser_handle);
 }
diff --git a/nmea.h b/nmea.h
index 98624269c9e25c0b91aaed633d19a79332c2b9c6..3da5acf606ed53a1fe0399734401556bf10b16ce 100644 (file)
--- a/nmea.h
+++ b/nmea.h
 #ifndef NMEA_H_INCLUDED_
 #define NMEA_H_INCLUDED_
 
-#include <ctime>                   // for gmtime
-
-#include <QtCore/QList>            // for QList
-#include <QtCore/QString>          // for QString, QString::KeepEmptyParts
-#include <QtCore/QVector>          // for QVector
+#include <QtCore/QByteArray>  // for QByteArray
+#include <QtCore/QDate>       // for QDate
+#include <QtCore/QDateTime>   // for QDateTime
+#include <QtCore/QList>       // for QList
+#include <QtCore/QString>     // for QString
+#include <QtCore/QTime>       // for QTime
+#include <QtCore/QVector>     // for QVector
 
 #include "defs.h"
-#include "format.h"
-#include "gbfile.h"                // for gbfprintf, gbfflush, gbfclose, gbfopen, gbfgetstr, gbfile
+#include "format.h"           // for Format
+#include "gbfile.h"           // for gbfile
 
 
 class NmeaFormat : public Format
@@ -103,19 +105,20 @@ private:
   Waypoint* nmea_new_wpt();
   void nmea_add_wpt(Waypoint* wpt, route_head* trk) const;
   static void nmea_release_wpt(Waypoint* wpt);
-  void nmea_set_waypoint_time(Waypoint* wpt, tm* time, double fsec);
-  void gpgll_parse(char* ibuf);
-  void gpgga_parse(char* ibuf);
-  void gprmc_parse(char* ibuf);
-  void gpwpl_parse(char* ibuf);
-  void gpzda_parse(char* ibuf);
-  void gpgsa_parse(char* ibuf) const;
-  void gpvtg_parse(char* ibuf) const;
+  void nmea_set_waypoint_time(Waypoint* wpt, QDateTime* prev, const QDate& date, const QTime& time);
+  static QTime nmea_parse_hms(const QString& str);
+  void gpgll_parse(const QString& ibuf);
+  void gpgga_parse(const QString& ibuf);
+  void gprmc_parse(const QString& ibuf);
+  void gpwpl_parse(const QString& ibuf);
+  void gpzda_parse(const QString& ibuf);
+  void gpgsa_parse(const QString& ibuf) const;
+  void gpvtg_parse(const QString& ibuf) const;
   static double pcmpt_deg(int d);
-  void pcmpt_parse(char* ibuf);
+  void pcmpt_parse(const char* ibuf);
   void nmea_fix_timestamps(route_head* track);
-  static int notalkerid_strmatch(const char* s1, const char* sentenceFormatterMnemonicCode);
-  void nmea_parse_one_line(char* ibuf);
+  static bool notalkerid_strmatch(const QByteArray& s1, const char* sentenceFormatterMnemonicCode);
+  void nmea_parse_one_line(const QByteArray& ibuf);
   static void safe_print(int cnt, const char* b);
   int hunt_sirf();
   void nmea_wayptpr(const Waypoint* wpt) const;
@@ -130,7 +133,7 @@ private:
   route_head* trk_head{};
   short_handle mkshort_handle{};
   preferred_posn_type posn_type;
-  struct tm tm{};
+  QDateTime prev_datetime;
   Waypoint* curr_waypt{};
   Waypoint* last_waypt{};
   void* gbser_handle{};
@@ -138,7 +141,7 @@ private:
   QList<Waypoint*> pcmpt_head;
 
   int without_date{};  /* number of created trackpoints without a valid date */
-  struct tm opt_tm{};  /* converted "date" parameter */
+  QDate opt_tm;        /* converted "date" parameter */
 
   char* opt_gprmc{};
   char* opt_gpgga{};
@@ -153,18 +156,16 @@ private:
   char* opt_gisteq{};
   char* opt_ignorefix{};
 
-  long sleepus{};
+  long sleepms{};
   int getposn{};
   int append_output{};
   bool amod_waypoint{};
 
-  time_t last_time{};
-  double last_read_time{};   /* Last timestamp of GGA or PRMC */
+  QDateTime last_write_time;
+  bool first_trkpt{};
+  QTime last_read_time;   /* Last timestamp of GGL, GGA or RMC */
   int datum{};
-  int had_checksum{};
-
-  Waypoint* nmea_rd_posn(posn_status*);
-  void nmea_rd_posn_init(const QString& fname);
+  bool had_checksum{};
 
   int wpt_not_added_yet{};
 
diff --git a/reference/track/addmultipledays.csv b/reference/track/addmultipledays.csv
new file mode 100644 (file)
index 0000000..bc29dcb
--- /dev/null
@@ -0,0 +1,29 @@
+No,Latitude,Longitude,Altitude,FIX,HDOP,Satellites,Date,Time\r
+1,50.224600,17.174617,468.8,"3d",0.80,9,2019/03/08,22:00:00\r
+2,50.224600,17.174617,468.8,"3d",0.80,9,2019/03/08,23:00:00\r
+3,50.224600,17.174617,468.8,"3d",0.80,9,2019/03/09,00:00:00\r
+4,50.224600,17.174617,468.8,"3d",0.80,9,2019/03/09,01:00:00\r
+5,50.224600,17.174617,468.8,"3d",0.80,9,2019/03/09,02:00:00\r
+6,50.224600,17.174617,468.8,"3d",0.80,9,2019/03/09,03:00:00\r
+7,50.224600,17.174617,468.8,"3d",0.80,9,2019/03/09,04:00:00\r
+8,50.224600,17.174617,468.8,"3d",0.80,9,2019/03/09,05:00:00\r
+9,50.224600,17.174617,468.8,"3d",0.80,9,2019/03/09,06:00:00\r
+10,50.224600,17.174617,468.8,"3d",0.80,9,2019/03/09,07:00:00\r
+11,50.224600,17.174617,468.8,"3d",0.80,9,2019/03/09,08:00:00\r
+12,50.224600,17.174617,468.8,"3d",0.80,9,2019/03/09,09:00:00\r
+13,50.224600,17.174617,468.8,"3d",0.80,9,2019/03/09,10:00:00\r
+14,50.224600,17.174617,468.8,"3d",0.80,9,2019/03/09,11:00:00\r
+15,50.224600,17.174617,468.8,"3d",0.80,9,2019/03/09,12:00:00\r
+16,50.224600,17.174617,468.8,"3d",0.80,9,2019/03/09,13:00:00\r
+17,50.224600,17.174617,468.8,"3d",0.80,9,2019/03/09,14:00:00\r
+18,50.224600,17.174617,468.8,"3d",0.80,9,2019/03/09,15:00:00\r
+19,50.224600,17.174617,468.8,"3d",0.80,9,2019/03/09,16:00:00\r
+20,50.224600,17.174617,468.8,"3d",0.80,9,2019/03/09,17:00:00\r
+21,50.224600,17.174617,468.8,"3d",0.80,9,2019/03/09,18:00:00\r
+22,50.224600,17.174617,468.8,"3d",0.80,9,2019/03/09,19:00:00\r
+23,50.224600,17.174617,468.8,"3d",0.80,9,2019/03/09,20:00:00\r
+24,50.224600,17.174617,468.8,"3d",0.80,9,2019/03/09,21:00:00\r
+25,50.224600,17.174617,468.8,"3d",0.80,9,2019/03/09,22:00:00\r
+26,50.224600,17.174617,468.8,"3d",0.80,9,2019/03/09,23:00:00\r
+27,50.224600,17.174617,468.8,"3d",0.80,9,2019/03/10,00:00:00\r
+28,50.224600,17.174617,468.8,"3d",0.80,9,2019/03/10,01:00:00\r
diff --git a/reference/track/addmultipledaysbkw.nmea b/reference/track/addmultipledaysbkw.nmea
new file mode 100644 (file)
index 0000000..83c06a7
--- /dev/null
@@ -0,0 +1,29 @@
+$GPGGA,220000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*66
+$GPGGA,230000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*67
+$GPGGA,000000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*66
+$GPGGA,010000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*67
+$GPGGA,020000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*64
+$GPGGA,030000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*65
+$GPGGA,040000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*62
+$GPGGA,050000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*63
+$GPGGA,060000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*60
+$GPGGA,070000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*61
+$GPGGA,080000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*6e
+$GPGGA,090000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*6f
+$GPGGA,100000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*67
+$GPGGA,110000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*66
+$GPGGA,120000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*65
+$GPGGA,130000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*64
+$GPGGA,140000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*63
+$GPGGA,150000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*62
+$GPGGA,160000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*61
+$GPGGA,170000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*60
+$GPGGA,180000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*6f
+$GPGGA,190000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*6e
+$GPGGA,200000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*64
+$GPGGA,210000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*65
+$GPGGA,220000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*66
+$GPGGA,230000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*67
+$GPGGA,000000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*66
+$GPGGA,010000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*67
+$GPZDA,033141,10,03,2019,00,00*44
diff --git a/reference/track/addmultipledaysfwd.nmea b/reference/track/addmultipledaysfwd.nmea
new file mode 100644 (file)
index 0000000..25e3081
--- /dev/null
@@ -0,0 +1,28 @@
+$GPGGA,220000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*66
+$GPGGA,230000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*67
+$GPGGA,000000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*66
+$GPGGA,010000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*67
+$GPGGA,020000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*64
+$GPGGA,030000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*65
+$GPGGA,040000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*62
+$GPGGA,050000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*63
+$GPGGA,060000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*60
+$GPGGA,070000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*61
+$GPGGA,080000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*6e
+$GPGGA,090000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*6f
+$GPGGA,100000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*67
+$GPGGA,110000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*66
+$GPGGA,120000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*65
+$GPGGA,130000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*64
+$GPGGA,140000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*63
+$GPGGA,150000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*62
+$GPGGA,160000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*61
+$GPGGA,170000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*60
+$GPGGA,180000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*6f
+$GPGGA,190000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*6e
+$GPGGA,200000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*64
+$GPGGA,210000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*65
+$GPGGA,220000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*66
+$GPGGA,230000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*67
+$GPGGA,000000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*66
+$GPGGA,010000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*67
diff --git a/reference/track/addmultipledaysmid.nmea b/reference/track/addmultipledaysmid.nmea
new file mode 100644 (file)
index 0000000..61d1469
--- /dev/null
@@ -0,0 +1,29 @@
+$GPGGA,220000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*66
+$GPGGA,230000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*67
+$GPGGA,000000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*66
+$GPGGA,010000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*67
+$GPGGA,020000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*64
+$GPGGA,030000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*65
+$GPGGA,040000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*62
+$GPGGA,050000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*63
+$GPGGA,060000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*60
+$GPGGA,070000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*61
+$GPGGA,080000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*6e
+$GPGGA,090000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*6f
+$GPGGA,100000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*67
+$GPZDA,103000,09,03,2019,00,00*4a
+$GPGGA,110000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*66
+$GPGGA,120000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*65
+$GPGGA,130000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*64
+$GPGGA,140000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*63
+$GPGGA,150000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*62
+$GPGGA,160000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*61
+$GPGGA,170000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*60
+$GPGGA,180000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*6f
+$GPGGA,190000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*6e
+$GPGGA,200000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*64
+$GPGGA,210000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*65
+$GPGGA,220000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*66
+$GPGGA,230000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*67
+$GPGGA,000000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*66
+$GPGGA,010000.000,5013.476,N,01710.477,E,1,09,0.8,468.770,M,0.0,M,,*67
index 3ef9e647f262eff9dd62488755d7867e7af120ba..785de58907775b03f9f689b66d221016a6c73426 100644 (file)
@@ -31,3 +31,11 @@ compare ${REFERENCE}/track/backfilldate2.csv ${TMPDIR}/backfilldate2.csv
 
 gpsbabel -i gpx -f ${REFERENCE}/track/nmeadate.gpx -o nmea -F ${TMPDIR}/nmeadate.nmea
 compare ${REFERENCE}/track/nmeadate.nmea ${TMPDIR}/nmeadate.nmea
+
+
+gpsbabel -t -i nmea,date=20190308 -f ${REFERENCE}/track/addmultipledaysfwd.nmea -o unicsv,utc=0 -F ${TMPDIR}/addmultipledaysfwd.csv
+compare ${REFERENCE}/track/addmultipledays.csv ${TMPDIR}/addmultipledaysfwd.csv
+gpsbabel -t -i nmea -f ${REFERENCE}/track/addmultipledaysbkw.nmea -o unicsv,utc=0 -F ${TMPDIR}/addmultipledaysbkw.csv
+compare ${REFERENCE}/track/addmultipledays.csv ${TMPDIR}/addmultipledaysbkw.csv
+gpsbabel -t -i nmea -f ${REFERENCE}/track/addmultipledaysmid.nmea -o unicsv,utc=0 -F ${TMPDIR}/addmultipledaysmid.csv
+compare ${REFERENCE}/track/addmultipledays.csv ${TMPDIR}/addmultipledaysmid.csv
index 72e42ec73d44439e9fb9ef0d111b4a2f0381deaf..88112a5dbd1563b6b0457ebde06b8a23cf02a594 100644 (file)
@@ -10,7 +10,7 @@ either a whole number of seconds or a fraction (e.g. 0.5 for a 1/2 second
 pause between trackpoints.)
 </para>
 <para>
-If this option is specified without a value, the time between adjacent 
+If this option is specified with a negative value, the time between adjacent 
 trackpoints will be computed and used for the length of the pause.  That is,
 if your trackpoints are 5 seconds apart, GPSBabel will pause 5 seconds 
 between trackpoints.